#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <libgen.h>
#include <dirent.h>
#include <pthread.h>
#include <semaphore.h>
#include <assert.h>
#include <ctype.h>

#include <arpa/inet.h>

#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/vfs.h>
#include <sys/mount.h>

#include <linux/unistd.h>
#include <linux/if.h>
#include <linux/wireless.h>

#include <net/route.h>

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include "getopt.h"
#include "bb_api.h"
#include "prj_rpc.h"

#define PAIR_TIMEOUT_DFT 100


typedef enum
{
	DEBUG_LEVEL = 0,
	INFO_LEVEL,
	WARN_LEVEL,
	ERR_LEVEL,
	MAX_LOG_LEVEL
};

uint32_t u32_log_level_ctrl = ERR_LEVEL;

#define PRINT_DEBUG(fmt, args...)  \
		do{\
			if(u32_log_level_ctrl <=DEBUG_LEVEL){\
				printf("[APP DEBUG] [ %s, Line: %d ]  " fmt "\n", __FILE__, __LINE__,  ##args); \
			}\
		}while(0)

#define PRINT_INFO(fmt, args...)  \
		do{\
			if(u32_log_level_ctrl <=INFO_LEVEL){\
				printf("[APP INFO] [ %s, Line: %d ]  " fmt "\n", __FILE__, __LINE__,  ##args); \
			}\
		}while(0)

#define PRINT_WARN(fmt, args...)  \
		do{\
			if(u32_log_level_ctrl <=WARN_LEVEL){\
				printf("[APP INFO] [ %s, Line: %d ]  " fmt "\n", __FILE__, __LINE__,  ##args); \
			}\
		}while(0)

#define PRINT_ERR(fmt, args...)  \
		do{\
			if(u32_log_level_ctrl <=ERR_LEVEL){\
				printf("[APP ERR] [ %s, Line:%d ]  " fmt "\n", __FILE__, __LINE__,  ##args); \
			}\
		}while(0)

#define PRINT_TRACE(fmt, args...)  \
        do{\
            printf("[APP TRACE] [%s ,%s, Line:%d ]  " fmt "\n", __func__,__FILE__, __LINE__,	##args); \
        }while(0)


static bb_dev_handle_t *bb_handle;
static int slot = 0;//-1;   //default slot 0
static int p_exit = 0;
static int signal_exit = 0;

void repeater_test_Usage(char * name)
{
    printf("Usage: %s [options]\n", name);
    printf("Options:\n");
    printf("\t-h       Print help info \n");
    printf("\t-r       Role(refers to bb_role_e. 0:ap,1:dev.)\n");
    printf("\t-s       Slot id\n");
    printf("\t-p       Do pair\n");
    printf("\t-t       Pair timeout(unit:s)\n");
    printf("\t-A       Asyn mode\n");
    printf("\t-R       Repeater mode\n");
    printf("\t-b       reboot delay(unit:ms)\n");
}

void pair_cb(void* arg, void* user)
{
    bb_event_pair_result_t *event = (bb_event_pair_result_t *)arg;
    uint8_t* slot_id = (uint8_t*)user;

    printf("pair ret %d slot_id %d\n", event->ret, *slot_id);
    switch (event->ret) {
        case -2:
            printf("pair timeout!\n");
            break;
        case 0:
            printf("pair ok!\n");
            break;
        default:
            break;
    }

    p_exit = 1;
}

static void signal_hander(uint32_t signal)
{
    char name[64];
    bb_set_prj_dispatch_in_t set_prj;
	prj_rpc_hdr_t *hdr;
	prj_cmd_event_pair_t *event_pair;

    if(signal_exit == 0)
    {
        printf("__Application will exit by signal:%d, pid:%d\n", signal, getpid());

        /*stop the pair*/
        hdr=(prj_rpc_hdr_t *)set_prj.data;
        hdr->cmdid = PRJ_CMD_EVENT_PAIR_STOP;

        bb_ioctl(bb_handle, BB_SET_PRJ_DISPATCH, &set_prj, NULL);

        if (signal == SIGSEGV)
        {
            SIG_DFL(signal);
        }

        signal_exit = 1;
    }
}

uint64_t MID_UTIL_Get_Time_ms(void)
{
    struct timeval tv;
	uint64_t ts_ms;
    gettimeofday(&tv, NULL);
	ts_ms = (uint64_t)tv.tv_sec*1000 + tv.tv_usec/1000;
    return ts_ms;
}

int main(int argc, char ** argv)
{
	int s32Ret;
    int role = -1;
    bool is_asyn = false;
    bool is_pair = false;
    int32_t reboot_delay = -1;
    int exit_time = -1;
    int repeater_mode = -1;
    int dev_num;
    int wait_times = 0;
    bb_host_t* phost=NULL;
    bb_dev_t** devs=NULL;
    int32_t timeout = PAIR_TIMEOUT_DFT;
	bb_set_prj_dispatch_in_t set_prj;
	prj_rpc_hdr_t *hdr;
	prj_cmd_event_pair_t *event_pair;
    prj_cmd_set_role_t *set_role;
    prj_cmd_set_repeater_mode_t* set_repeater_mode;
    bb_event_callback pcb = (bb_event_callback)pair_cb;
    bb_set_event_callback_t cb;
    int ret;

    if(argc == 1){
	    repeater_test_Usage(argv[0]);
		return -1;
	}

    while((s32Ret = getopt(argc, argv, "hr:s:p:t:AR:b:")) != -1)
    {
        switch(s32Ret)
		{
            case 'h':
                repeater_test_Usage(argv[0]);
                return 0;
            case 'r':
                role = atoi(optarg);
                printf("role mode %d\n", role);
                break;
            case 's':
                slot = atoi(optarg);
                printf("slot bitmap 0x%x\n", slot);
                break;
            case 't':
                timeout = atoi(optarg);
                printf("timeout %d\n", timeout);
                break;
            case 'A':
                printf("is_asyn mode\n");
                is_asyn = true;
                break;
            case 'b':
                reboot_delay = atoi(optarg);
                printf("reboot delay %d\n", reboot_delay);
                break;
            case 'p':
                is_pair = true;
                printf("do pair\n");
                break;
            case 'R':
                repeater_mode = atoi(optarg);
                printf("repeater_mode %d\n", repeater_mode);
                break;
            default:
                PRINT_ERR("unknow cmd param !");
    			repeater_test_Usage(argv[0]);
                return -1;
        }
    }

    printf("%s compiled at: %s %s\n", argv[0], __DATE__, __TIME__);

    /* ==================Build connection with 8030(start)================ */
    /* Connect to the daemon */
	s32Ret = bb_host_connect(&phost,"127.0.0.1",BB_PORT_DEFAULT);
	if(s32Ret)
	{
		PRINT_ERR("bb connect error,ret=%d",s32Ret);
		//bb_deinit();
		return -1;
	}

    /* get dev list of the 8030 */
    dev_num = bb_dev_getlist(phost,&devs);
    if (dev_num == 0) {
		PRINT_ERR("Get no device!");
		return -1;
    }

    /* Open the dev 8030 */
    bb_handle = bb_dev_open(devs[0]);
    if (!bb_handle) {
		PRINT_ERR("bb_dev_open error");
		//bb_deinit();
		return -1;
    }

    bb_dev_freelist(devs);

    if (reboot_delay != -1) {
        bb_ioctl(bb_handle, BB_SET_SYS_REBOOT, &reboot_delay, NULL);

        return 0;
    }

    if (is_pair) {
        cb.callback = pcb;
        cb.event    = BB_EVENT_PAIR_RESULT;
        cb.user     = (void*)&slot;
        s32Ret = bb_ioctl(bb_handle, BB_SET_EVENT_SUBSCRIBE, &cb, 0);
        if (s32Ret) {
            PRINT_ERR("set callback err! ret = %d",s32Ret );
            return -1;
        }
        else {
            printf("set callback success!\n");
        }
    }

    printf("Build connection with 8030 success!\n");
    /* ==================Build connection with 8030(end)================ */

    if (role != -1) {
        hdr=(prj_rpc_hdr_t *)set_prj.data;
        hdr->cmdid = PRJ_CMD_SET_ROLE;
        set_role = (prj_cmd_set_role_t*)hdr->data;
        set_role->role = role;

        s32Ret = bb_ioctl(bb_handle, BB_SET_PRJ_DISPATCH, &set_prj, NULL);
        if (s32Ret) {
            PRINT_ERR("set role err! ret = %d",s32Ret);
        }
        else {
            printf("set role %d success!\n", role);
        }
    }

    if (repeater_mode != -1) {
        hdr=(prj_rpc_hdr_t *)set_prj.data;
        hdr->cmdid = PRJ_CMD_SET_REPEATER_MODE;
        set_repeater_mode = (prj_cmd_set_repeater_mode_t*)hdr->data;
        set_repeater_mode->repeater_mode = repeater_mode;

        s32Ret = bb_ioctl(bb_handle, BB_SET_PRJ_DISPATCH, &set_prj, NULL);
        if (s32Ret) {
            PRINT_ERR("set repeater_mode err! ret = %d",s32Ret);
        }
        else {
            printf("set repeater_mode %d success!\n", repeater_mode);
        }
    }

    if (is_pair) {
        /* begin to pair */
        hdr=(prj_rpc_hdr_t *)set_prj.data;
        hdr->cmdid=PRJ_CMD_EVENT_PAIR;
        event_pair = (prj_cmd_event_pair_t*)hdr->data;
        event_pair->bitmap = (1 << slot);
        event_pair->timeout = timeout;
        event_pair->asyn = is_asyn;

        bb_ioctl(bb_handle, BB_SET_PRJ_DISPATCH, &set_prj, NULL);

        /*wait for reult*/
        while (is_asyn) {
            if (!p_exit && !signal_exit) {
                printf("waiting for pair\n");
            }
            else {
                break;
            }

            if (exit_time == wait_times) {
                /*stop the pair*/
                hdr=(prj_rpc_hdr_t *)set_prj.data;
                hdr->cmdid = PRJ_CMD_EVENT_PAIR_STOP;

                bb_ioctl(bb_handle, BB_SET_PRJ_DISPATCH, &set_prj, NULL);
            }

            sleep(1);
            wait_times++;
        }

        if (signal_exit) {
            exit(0);
        }

        printf("==== pair end !!!\n");
    }

    printf("test end, exit!\n");

    exit(0);
}
